package org.example.spi;

import cn.hutool.core.io.resource.ResourceUtil;
import lombok.extern.slf4j.Slf4j;
import org.example.serializer.Serializer;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * SPI加载器相关配置与缓存
 */
@Slf4j
public class SpiLoader {

    /**
     * 存储已加载的类：接口名=>（key=>实现类）
     */
    private static Map<String, Map<String, Class<?>>> loaderMap = new ConcurrentHashMap<>();

    /**
     * 对象实例缓存（避免重复new），类路径=>对象实例，单例模式
     */
    private static Map<String, Object> instanceCache = new ConcurrentHashMap<>();

    /**
     * 系统SPI目录
     */
    private static final String RPC_SYSTEM_SPI_DIR = "rpc/system/";

    /**
     * 用户自定义SPI目录
     */
    private static final String RPC_CUSTOM_SPI_DIR = "rpc/custom/";

    /**
     * 扫描路径（包含系统SPI目录和用户自定义SPI目录）
     */
    private static final String[] SCAN_DIRS = new String[]{RPC_SYSTEM_SPI_DIR, RPC_CUSTOM_SPI_DIR};

    public static Map<String, Class<?>> load(Class<?> loadClass) {
        log.info("加载类型为{}的SPI", loadClass.getName());

        // 扫描路径，用户自定义的SPI优先级高于系统SPI
        Map<String, Class<?>> keyClassMap = new HashMap<>();

        for (String scanDir : SCAN_DIRS) {
            // 读取扫描目录下与接口名对应的资源文件（如META-INF/rpc/custom/xxx.Serializer）
            List<URL> resources = ResourceUtil.getResources(scanDir + loadClass.getName());
            log.info("扫描目录：{}", scanDir+loadClass.getName());
            // 读取每个资源文件内容
            for (URL resource : resources) {
                try (InputStreamReader inputStreamReader = new InputStreamReader(resource.openStream());
                     BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {

                    String line;
                    // 按行读取资源文件（格式：key=实现类全类名）
                    while ((line = bufferedReader.readLine()) != null) {
                        line = line.trim(); // 去除空白字符
                        if (line.isEmpty()) {
                            continue; // 跳过空行
                        }

                        String[] strArray = line.split("=");
                        if (strArray.length == 2) { // 确保格式正确（key和value都存在）
                            String key = strArray[0].trim();
                            String className = strArray[1].trim();
                            // 加载类并放入映射（后加载的用户自定义SPI会覆盖系统SPI的同名key）
                            keyClassMap.put(key, Class.forName(className));
                        }
                    }
                } catch (Exception e) {
                    log.error("SPI资源加载失败", e);
                }
            }
        }

        // 将加载结果存入全局缓存
        loaderMap.put(loadClass.getName(), keyClassMap);
        return keyClassMap;
    }
    public static <T> T getInstance(Class<?> tClass, String key) {
        // 1. 获取接口全类名，从loaderMap中查询该接口的实现类映射
        String tClassName = tClass.getName();
        Map<String, Class<?>> keyClassMap = loaderMap.get(tClassName);

        // 2. 校验：若该接口未加载过SPI，抛异常
        if (keyClassMap == null) {
            throw new RuntimeException(String.format("SpiLoader 未加载 %s 接口的SPI实现", tClassName));
        }

        // 3. 校验：若指定key不存在于实现类映射中，抛异常
        if (!keyClassMap.containsKey(key)) {
            throw new RuntimeException(String.format("SpiLoader 的 %s 接口下，不存在key为【%s】的实现类", tClassName, key));
        }

        // 4. 获取目标实现类的Class对象
        Class<?> implClass = keyClassMap.get(key);
        String implClassName = implClass.getName();

        // 5. 从实例缓存获取：若未缓存，实例化后存入缓存（保证单例，处理并发）
        if (!instanceCache.containsKey(implClassName)) {
            try {
                // 反射实例化（需确保实现类有默认无参构造）
                Object instance = implClass.newInstance();
                // 用putIfAbsent避免并发下重复实例化（配合ConcurrentHashMap）
                instanceCache.putIfAbsent(implClassName, instance);
            } catch (InstantiationException | IllegalAccessException e) {
                throw new RuntimeException(String.format("%s 类实例化失败（需确保有默认无参构造）", implClassName), e);
            }
        }

        // 6. 强制转换为泛型类型并返回
        return (T) instanceCache.get(implClassName);
    }
}